// Fill out your copyright notice in the Description page of Project Settings.


#include "Layers/MotionSubtitlesTextLayer.h"
#include "SdfGenUtils.h"
#include "TextInfos.h"
#include "TextLayoutUtils.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/Texture2DDynamic.h"


UMotionSubtitlesTextLayer::UMotionSubtitlesTextLayer()
{
	PrimaryComponentTick.bCanEverTick = true;

	struct FConstructorStatics
	{
		ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFinder;
		ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialFinder;

		FConstructorStatics(): MeshFinder(TEXT("/Script/Engine.StaticMesh'/MotionSubtitles/StaticMeshes/SM_Plane.SM_Plane'")),
		                       MaterialFinder(TEXT("/Script/Engine.Material'/MotionSubtitles/Materials/M_StylizedText_Update.M_StylizedText_Update'"))
		{
		}
	};

	static FConstructorStatics ConstructorStatics;
	if (ConstructorStatics.MeshFinder.Succeeded())
	{
		PlaneStaticMesh = ConstructorStatics.MeshFinder.Object;
	}

	if (ConstructorStatics.MaterialFinder.Succeeded())
	{
		StylizedTextMaterial = ConstructorStatics.MaterialFinder.Object;
	}

	LayerMask = ELayerMask::Layer1;
}

void UMotionSubtitlesTextLayer::Init(const FUint32Vector2& ViewSize)
{
	Super::Init(ViewSize);
}

void UMotionSubtitlesTextLayer::Show(const FSubtitleParams& Params)
{
	Super::Show(Params);

	check(PlaneStaticMesh);
	check(StylizedTextMaterial);

	Contents = Params.Contents;

	Hide();

	// 查找TTF文件
	FString FontPath;
	if (!FindTtfFile(FontName, FontPath))
	{
		return;
	}

	// 暂时只支持2行
	if (Contents.Num() > 2)
	{
		UE_LOG(LogTemp, Error, TEXT("UMotionSubtitlesTextLayer 暂时只支持拆分为两行"))
		return;
	}

	// 根据Text属性构造TextPlanes
	TQueue<FSubtitleLineParams> Lines;
	for (int32 i = 0; i < Contents.Num(); i++)
	{
		if (TArray<FTextFontInfo> Infos; GenerateTextPlanes(Contents[i], FontPath, Infos))
		{
			if (!Infos.IsEmpty())
			{
				FSubtitleLineParams Line;
				Line.FontInfos = Infos;
				Line.Offset = Contents[i].Offset;
				Lines.Enqueue(Line);
			}
		}
	}

	// 计算文字布局
	LayoutText(Lines);
}

void UMotionSubtitlesTextLayer::Hide()
{
	Super::Hide();

	if (TextPlanes.Num() == 0)
	{
		return;
	}

	for (int32 i = 0; i < TextPlanes.Num(); i++)
	{
		SceneCaptureComponent2D->RemoveShowOnlyComponent(TextPlanes[i]);
		TextPlanes[i]->DestroyComponent();
	}
	TextPlanes.Empty();
}

void UMotionSubtitlesTextLayer::Exit()
{
	Super::Exit();
}

void UMotionSubtitlesTextLayer::DoScale(const EAnimTarget AnimTarget, const int32 ElementId, const float Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		SetRelativeScale3D(OriginScale * Value);
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			Element->SetRelativeScale3D(TextOriginScales[ElementId] * Value);
		}
	}
}

void UMotionSubtitlesTextLayer::DoTranslate(const EAnimTarget AnimTarget, const int32 ElementId, const FVector Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		for (int32 i = 0; i < TextPlanes.Num(); i++)
		{
			TextPlanes[i]->SetRelativeLocation(TextOriginLocations[i] + Value);
		}
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			Element->SetRelativeLocation(TextOriginLocations[ElementId] + Value);
		}
	}
}

void UMotionSubtitlesTextLayer::DoFade(const EAnimTarget AnimTarget, const int32 ElementId, const float Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		for (int32 i = 0; i < TextPlanes.Num(); i++)
		{
			UMaterialInterface* Material = TextPlanes[i]->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("AlphaFactor"), Value);
			}
		}
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			UMaterialInterface* Material = Element->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("AlphaFactor"), Value);
			}
		}
	}
}

void UMotionSubtitlesTextLayer::DoDissolve(const EAnimTarget AnimTarget, const int32 ElementId, const float Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		for (const auto Element : TextPlanes)
		{
			UMaterialInterface* Material = Element->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("Dissolve"), Value);
			}
		}
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			UMaterialInterface* Material = Element->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("Dissolve"), Value);
			}
		}
	}
}

void UMotionSubtitlesTextLayer::DoMarquee(const EAnimTarget AnimTarget,const EAnimDirection Direction, const int32 ElementId, const float Value)
{
	if (AnimTarget == EAnimTarget::WholeLayer)
	{
		// 文字走马灯效果，暂不支持整层
	}
	else
	{
		if (TextPlanes.IsValidIndex(ElementId))
		{
			const auto Element = TextPlanes[ElementId];
			UMaterialInterface* Material = Element->GetMaterial(0);
			if (UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Material))
			{
				DynamicMaterial->SetScalarParameterValue(TEXT("Marquee"), Value);
				
				if(Direction == EAnimDirection::Left)
				{
					DynamicMaterial->SetScalarParameterValue(TEXT("MarqueeDirection"), 0.0);
				}
				
				if(Direction == EAnimDirection::Up)
				{
					DynamicMaterial->SetScalarParameterValue(TEXT("MarqueeDirection"), 1);
				}
			}
		}
	}
}

void UMotionSubtitlesTextLayer::CaptureStatus()
{
	Super::CaptureStatus();

	TextOriginLocations.Empty();
	TextOriginScales.Empty();
	for (int32 i = 0; i < TextPlanes.Num(); i++)
	{
		// 记录文字初始相对位置
		TextOriginLocations.Add(TextPlanes[i]->GetRelativeLocation());
		TextOriginScales.Add(TextPlanes[i]->GetRelativeScale3D());
	}
}

int32 UMotionSubtitlesTextLayer::ElementCount()
{
	return TextPlanes.Num();
}


bool UMotionSubtitlesTextLayer::GenerateSdf(const FString& SingleChar, const FString& FontPath,
                                            FTextFontInfo& FontInfo) const
{
	if (SingleChar.IsEmpty())
	{
		return false;
	}


	FSdfGenUtils::GenerateSDF(SingleChar, FontPath, FontInfo, FontSize, FontScale);
	FontInfo.FillingColor = FillingColor;
	FontInfo.OutlineColor = OutlineColor;
	FontInfo.ShadowColor = ShadowColor;

	return true;
}

bool UMotionSubtitlesTextLayer::GenerateMaterial(const FTextFontInfo& FontInfo,
                                                 UMaterialInstanceDynamic*& MaterialInstance)
{
	if (FontInfo.Bitmap == nullptr || StylizedTextMaterial == nullptr)
	{
		return false;
	}

	MaterialInstance = UMaterialInstanceDynamic::Create(StylizedTextMaterial, this);
	MaterialInstance->SetTextureParameterValue(TEXT("TextMap"), FontInfo.Bitmap);
	MaterialInstance->SetVectorParameterValue(TEXT("FillingColor"), FontInfo.FillingColor);
	MaterialInstance->SetVectorParameterValue(TEXT("OutlineColor"), FontInfo.OutlineColor);
	MaterialInstance->SetVectorParameterValue(TEXT("MarqueeColor"), MarqueeColor);
	MaterialInstance->SetScalarParameterValue(TEXT("OutlineWidth"), OutlineWidth);
	MaterialInstance->SetVectorParameterValue(TEXT("ShadowColor"), ShadowColor);
	MaterialInstance->SetScalarParameterValue(TEXT("ShadowBiasX"), ShadowBiasX);
	MaterialInstance->SetScalarParameterValue(TEXT("ShadowBiasY"), ShadowBiasY);
	MaterialInstance->SetScalarParameterValue(TEXT("Alpha"), Alpha);
	MaterialInstance->SetScalarParameterValue(TEXT("Blur"), Blur);
	MaterialInstance->SetScalarParameterValue(TEXT("Dissolve"), Dissolve);
	return true;
}

bool UMotionSubtitlesTextLayer::GenerateTextMesh(UStaticMesh* StaticMesh, UMaterialInstanceDynamic* Material,
                                                 UStaticMeshComponent*& StaticMeshComponent)
{
	if (StaticMesh == nullptr || Material == nullptr)
	{
		return false;
	}

	StaticMeshComponent = NewObject<UStaticMeshComponent>(this);
	StaticMeshComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);
	StaticMeshComponent->RegisterComponent();
	StaticMeshComponent->bCastStaticShadow = false;
	StaticMeshComponent->bCastDynamicShadow = false;
	StaticMeshComponent->bVisibleInSceneCaptureOnly = true;
	StaticMeshComponent->SetHiddenInSceneCapture(false);
	StaticMeshComponent->SetStaticMesh(StaticMesh);
	StaticMeshComponent->SetMaterial(0, Material);
	return true;
}

bool UMotionSubtitlesTextLayer::GenerateTextPlanes(const FSubtitleContent& Content, const FString& DefaultFont,
                                                   TArray<FTextFontInfo>& Infos)
{
	const FString Line = Content.Text;
	for (int32 i = 0; i < Line.Len(); i++)
	{
		// 获取Style参数
		FTextStyle* Style = nullptr;
		FindTextStyle(Content.Styles, i, Style);

		// 使用Style参数覆盖文字字体
		FString Font = DefaultFont;
		OverrideTtfFile(Style, Font);

		// 获取当前字符
		FString SingleChar = Line.Mid(i, 1);
		FTextFontInfo FontInfo;
		if (!GenerateSdf(SingleChar, Font, FontInfo))
		{
			continue;
		}

		// 使用Style参数覆盖文字FillingColor
		FontInfo.FillingColor = FillingColor;
		OverrideFillingColor(Style, FontInfo);

		// 使用Style参数覆盖文字OutlineColor
		FontInfo.OutlineColor = OutlineColor;
		OverrideOutlineColor(Style, FontInfo);

		// 使用Style参数覆盖文字Enhance
		FontInfo.Enhance = 1.0f;
		OverrideEnhance(Style, FontInfo);

		// 生成字体的材质
		UMaterialInstanceDynamic* TextMaterial;
		if (!GenerateMaterial(FontInfo, TextMaterial))
		{
			continue;
		}

		// 生成字体的网格
		UStaticMeshComponent* StaticMeshComponent;
		if (!GenerateTextMesh(PlaneStaticMesh, TextMaterial, StaticMeshComponent))
		{
			continue;
		}
		SceneCaptureComponent2D->ShowOnlyComponent(StaticMeshComponent);
		TextPlanes.Add(StaticMeshComponent);

		FontInfo.TextPlane = StaticMeshComponent;

		Infos.Add(FontInfo);
	}

	return true;
}

void UMotionSubtitlesTextLayer::LayoutText(TQueue<FSubtitleLineParams>& Lines) const
{
	// 计算文字布局
	float Spacing = 0;
	while (!Lines.IsEmpty())
	{
		FSubtitleLineParams Line;
		if (!Lines.Dequeue(Line))
		{
			UE_LOG(LogTemp, Error, TEXT("UMotionSubtitlesTextLayer 获取子列数据失败"))
			continue;
		}

		// 计算文字偏移
		FVector2f Offset = FVector2f::Zero();
		if (TextLayoutInfo.Mode == ELayoutMode::Vertical)
		{
			Offset.X += Spacing;
			Offset.Y = Line.Offset * FontSize * FontScale;
		}
		else
		{
			Offset.X = Line.Offset * FontSize * FontScale;
			Offset.Y -= Spacing;
		}
		Spacing = TextLayoutInfo.Spacing * FontSize * FontScale;
        
		LayoutText(Line.FontInfos, Offset);
	}
}

void UMotionSubtitlesTextLayer::LayoutText(const TArray<FTextFontInfo>& Infos, const FVector2f& Offset) const
{
	if (Infos.Num() <= 0)
	{
		return;
	}

	TArray<FVector> TextPositions;
	TextPositions.Init(FVector::ZeroVector, Infos.Num());

	const bool VerticalLayout = (TextLayoutInfo.Mode == ELayoutMode::Vertical) ? true : false;
	FTextLayoutUtils::CalculateLayout(TextLayoutInfo, Infos, VerticalLayout, TextPositions);

	for (auto i = 0; i < Infos.Num(); ++i)
	{
		TWeakObjectPtr<UStaticMeshComponent> VfxTextComponent = Infos[i].TextPlane;
		if (VfxTextComponent != nullptr)
		{
			const FVector& Pos = TextPositions[i];
			VfxTextComponent->SetRelativeLocation(FVector(0, Pos.X + Offset.X, -Pos.Y + Offset.Y));
			VfxTextComponent->SetRelativeRotation(FRotator(0, 90, 90));

			VfxTextComponent->SetRelativeScale3D(FVector(
				(Infos[i].Width * Infos[i].Enhance * Infos[i].AdvanceH) / 100.0,
				(Infos[i].Height * Infos[i].Enhance * Infos[i].AdvanceV) / 100.0,
				1.0));
		}
	}
}

bool UMotionSubtitlesTextLayer::FindTtfFile(const FString& Font, FString& RealPath)
{
	if (Font.IsEmpty())
	{
		return false;
	}

	// 获取TTF文件的完整路径
	const FString ContentDir = FPaths::ProjectContentDir();
	const FString ContentFullPath = FPaths::ConvertRelativePathToFull(ContentDir);
	RealPath = FPaths::Combine(ContentFullPath,TEXT("StreamingAssets/TTF"), Font);

	IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
	if (RealPath.IsEmpty() || !RealPath.EndsWith(".ttf") || !PlatformFile.FileExists(*RealPath))
	{
		return false;
	}
	return true;
}

bool UMotionSubtitlesTextLayer::FindTextStyle(const TArray<FTextStyle>& Styles, const int32 CharIndex,
                                              FTextStyle*& Style)
{
	for (int32 j = 0; j < Styles.Num(); j++)
	{
		if (Styles[j].Index == CharIndex)
		{
			Style = const_cast<FTextStyle*>(&Styles[j]);
			return true;
		}
	}
	return false;
}

void UMotionSubtitlesTextLayer::OverrideTtfFile(const FTextStyle* Style, FString& Font)
{
	// 获取字体路径
	if (Style != nullptr)
	{
		FString OverrideFont;
		if (FindTtfFile(Style->Font, OverrideFont))
		{
			Font = OverrideFont;
		}
	}
}

void UMotionSubtitlesTextLayer::OverrideFillingColor(const FTextStyle* Style, FTextFontInfo& FontInfo)
{
	if (Style != nullptr)
	{
		if (Style->FillingColor != FLinearColor::Transparent)
		{
			FontInfo.FillingColor = Style->FillingColor;
		}
	}
}

void UMotionSubtitlesTextLayer::OverrideOutlineColor(const FTextStyle* Style, FTextFontInfo& FontInfo)
{
	if (Style != nullptr)
	{
		if (Style->OutlineColor != FLinearColor::Transparent)
		{
			FontInfo.OutlineColor = Style->OutlineColor;
		}
	}
}

void UMotionSubtitlesTextLayer::OverrideEnhance(const FTextStyle* Style, FTextFontInfo& FontInfo)
{
	if (Style != nullptr)
	{
		 if(Style->IsEnhance)
         {
        	FontInfo.Enhance = TextLayoutInfo.Enhance;
         }
	}
}
